﻿namespace NavigateTo2013
{
    using Microsoft.VisualStudio.Language.NavigateTo.Interfaces;
    using System.Collections.Generic;
    using System.Data.OleDb;
    using System.Threading;

    public class NavigateToItemProvider : INavigateToItemProvider
    {
        string connectionString = "Provider=Search.CollatorDSO;Extended Properties=\"Application=Windows\"";

        Queue<Task> queue = new Queue<Task>();

        Thread worker;

        bool isDisposed;

        public NavigateToItemProvider()
        {
            this.GetOptions();
            worker = new Thread(Work);
            worker.Start();                        
        }

        void Work()
        {
            try
            {
                using (var connection = new OleDbConnection(connectionString))
                {
                    connection.Open();
                    while (true)
                    {
                        if (isDisposed)
                            break;

                        if (queue.Count == 0)
                        {
                            Thread.Yield();
                            continue;
                        }

                        var task = queue.Peek();
                        if (!task.IsCancelled)
                        {
                            try
                            {
                                task.Connection = connection;
                                SearchEngine.Search(task);
                            }
                            catch (OleDbException)
                            {
                                // Ingore this and continue searching
                            }
                        }

                        queue.Dequeue();
                    }
                }
            }
            catch
            {
                // Ignore this so that vs doesn't crash
            }
        }

        public NavigateToAllOptions Options { get; set; }

        public void StartSearch(INavigateToCallback callback, string searchValue)
        {
            if (searchValue != null)
            {
                queue.Enqueue(new Task { Callback = callback, SearchValue = searchValue.Replace("'", "''"), Provider = this });
            }
        }

        public void StopSearch()
        {
            // Cancel all tasks started so far
            foreach (var task in queue.ToArray())
            {
                task.IsCancelled = true;
                task.Callback.Invalidate();
            }
        }

        public void RequeueTask(Task task)
        {
            queue.Enqueue(task);
        }

        public void Dispose()
        {
            isDisposed = true;
            worker.Join(2000);
        }

        private void GetOptions()
        {
            try
            {
                this.Options = new NavigateToAllOptions();
                var dte = (EnvDTE.DTE)ServiceProvider.Current.GetService(typeof(EnvDTE.DTE));
                var properties = dte.Properties["NavigateToAll", "General"];
                this.Options.RestrictToTheseFolders = (string)properties.Item("RestrictToTheseFolders").Value;
                this.Options.RestrictToSolutionFolder = (bool)properties.Item("RestrictToSolutionFolder").Value;
                this.Options.AllowedFileExtensions = (string)properties.Item("AllowedFileExtensions").Value;
            }
            catch
            {
                // ignore options and continue
            }
        }
    }
}